package 多线程.Java并发编程.producer_consumer.非阻塞队列实现;

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author wangxi created on 2023/12/30 22:53
 * @version v1.0
 *
 * 线程之所以不交替打印：当生产线程获取lock，生产，释放lock，然后进入while循环。由于线程偏向锁原因，生产线程
 * 仍然会获取锁进行生产，直到调用await方法释放锁。同理消费者一样的。
 *
 * 如果偏向锁不起效(偶尔貌似没生效)，那么就会交替打印
 */
public class MyBlockingQueueForCondition {
    private Queue<Integer> queue;

    private int max;

    private ReentrantLock lock = new ReentrantLock();

    private Condition notEmpty = lock.newCondition();

    private Condition notFull = lock.newCondition();

    public MyBlockingQueueForCondition(int size) {
        this.max = size;
        queue = new LinkedList<>();

    }

    public void put(int o) throws InterruptedException {
        while (true) {
            System.out.println("producer尝试获取锁. size = " + queue.size());
            lock.lock();
            System.out.println("producer获取锁. size = " + queue.size());
            try {
                while (queue.size() >= max) {
                    notFull.await();
                }
                Thread.sleep(1000);
                queue.add(o);
                System.out.println("put一个元素.size = " + queue.size());
                notEmpty.signalAll();
            } finally {
                lock.unlock();

            }
            // 在这里加一下sleep就会交替打印。因为此时锁释放了，消费者获取到锁就会消费
            // Thread.sleep(1000);
        }
    }

    public void take() throws InterruptedException {
        while (true) {
            System.out.println("consumer尝试获取锁. size = " + queue.size());
            lock.lock();
            System.out.println("consumer获取锁. size = " + queue.size());
            try {
                while (queue.size() <= 0) {
                    notEmpty.await();
                }
                Thread.sleep(1000);
                Object item = queue.remove();
                System.out.println("take一个元素.size = " + queue.size());
                notFull.signalAll();
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        MyBlockingQueueForCondition obj = new MyBlockingQueueForCondition(5);
        new Thread(() -> {
            try {
                obj.put(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        new Thread(() -> {
            try {
                obj.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}
